#include "main.h"
#include "def.h"
#include "sync.h"
#include "util.h"
#include "gui.h"
#include "net.h"
#include "plugin.h"

#include <maya/MGlobal.h>
#include <maya/MFnDagNode.h>
#include <maya/MDagPath.h>
#include <maya/MDGMessage.h>
#include <maya/MFnMesh.h>
#include <maya/MItDependencyNodes.h>
#include <maya/MSelectionList.h>
#include <maya/MDagMessage.h>
#include <maya/MEventMessage.h>
#include <maya/MCallbackIdArray.h>
#include <maya/MPlugArray.h>
#include <time.h>

#pragma message ("remove when VTRY/VCATCH refactored")
#include "..\Global\globaldef.h"

#pragma message ("todo reorganize 1")
#include "..\CommLib\PacketConven.h"
//extern uint GetObjectVertexFormat(MObject& object);

bool GetNameBuffer(tCharBuffer* pCharBuffer, MString string)
{
	if(!pCharBuffer) 
		return false;

	const char* c_str = string.asChar();
	if(!c_str)
		return false;

	memset(pCharBuffer->Buffer, 0, sizeof(pCharBuffer->Buffer));
	memcpy(pCharBuffer->Buffer, c_str, strlen(c_str));
	return true;
}

bool GetNameBuffer(tCharBuffer* pCharBuffer, MObject object)
{
	MStatus status;
	MFnDependencyNode objectNode(object, &status);
	if(MAYA_FAIL(status))
		return false;

	return GetNameBuffer(pCharBuffer, objectNode.name());
}

tCharBuffer GetMeshName(MObject& mesh) {

	tCharBuffer Buffer = {{'\0'}};
	if(!VUtil::IsMesh(mesh))
		return Buffer;

	GetNameBuffer(&Buffer, mesh);
	return Buffer;
}

bool IsEqual(const char* viewerString, MString& mayaString) {

	bool isEqual = false;
	u_int vsLength = strlen(viewerString);
	u_int msLength = mayaString.length();

	if(vsLength != msLength)
		return isEqual;

	isEqual = (mayaString == (const char*)viewerString);
	return isEqual;
}

MCallbackId		VSyncViewer::g_ViewportSelectionListenerCallback = 0;
MCallbackId		VSyncViewer::g_ViewportAddSceneNodeCallback = 0;
MCallbackId		VSyncViewer::g_ViewportAddDGNodeCallback = 0;

MIntArray VSyncViewer::g_RegisteredCallbacks;

long			VSyncViewer::g_SyncTimer = 0;

bool CommonMsgProcess(MNodeMessage::AttributeMessage msg, MPlug& plug) {
					  
	if(msg & (MNodeMessage::kConnectionMade | MNodeMessage::kAttributeArrayAdded))
		return true;

	if(msg & (MNodeMessage::kConnectionBroken | MNodeMessage::kAttributeArrayRemoved)) {
		
		VSyncViewer::CallbackWrappers::RemoveObjectCallbacks(plug.node());
		return true;
	}

	return false;
}

void VSyncViewer::InitPluginSelection() {

	VSyncViewer::OnChangeSelection(NULL);
}

void BridgeCall(MDagPath& node, MDagPath& parent, void* clientData) {
	
	VSyncViewer::AddNodeCallback(node.node(), clientData);
}

bool VSyncViewer::InitalizeObjectTypeCallbacks(MFn::Type type, MNodeMessage::MAttr2PlugFunction callback) {

	MObjectArray objects;
	if(VUtil::GetSceneObjects(objects, type) && callback) {
		
		for(uint i = 0; i < objects.length(); i++) {

			MStatus result;
			MCallbackId cb = MNodeMessage::addAttributeChangedCallback(objects[i], callback, 0, &result);
			MAYA_CONTINUE_IF_FAIL(result);

			VSyncViewer::g_RegisteredCallbacks.append(cb);
		}

		return true;
	}
	return false;
}

bool VSyncViewer::InitalizeCallbacks() {

	VSyncViewer::ReleaseCallbacks();

	MStatus result;
	VSyncViewer::g_ViewportSelectionListenerCallback = MEventMessage::addEventCallback("SelectionChanged", VSyncViewer::OnChangeSelection, NULL, &result);
	MAYA_RETURN_IF_FAIL(result, false);
	
	VSyncViewer::g_ViewportAddDGNodeCallback = MDGMessage::addNodeAddedCallback(VSyncViewer::AddNodeCallback);
	MAYA_RETURN_IF_FAIL(result, false);

	InitalizeObjectTypeCallbacks(MFn::kPointLight, VSyncViewer::CallbackWrappers::OnChangeLight);
	InitalizeObjectTypeCallbacks(MFn::kDirectionalLight, VSyncViewer::CallbackWrappers::OnChangeLight);

	InitalizeObjectTypeCallbacks(MFn::kTransform, VSyncViewer::CallbackWrappers::OnChangeTransform);
	InitalizeObjectTypeCallbacks(MFn::kMesh, VSyncViewer::CallbackWrappers::OnChangeMesh);
	InitalizeObjectTypeCallbacks(MFn::kMidModifier, VSyncViewer::CallbackWrappers::OnChangeUV);

	InitalizeObjectTypeCallbacks(MFn::kChecker, VSyncViewer::CallbackWrappers::OnChangeSampler);
	InitalizeObjectTypeCallbacks(MFn::kNoise, VSyncViewer::CallbackWrappers::OnChangeSampler);
	InitalizeObjectTypeCallbacks(MFn::kGrid, VSyncViewer::CallbackWrappers::OnChangeSampler);
	InitalizeObjectTypeCallbacks(MFn::kRamp, VSyncViewer::CallbackWrappers::OnChangeSampler);
	InitalizeObjectTypeCallbacks(MFn::kFileTexture, VSyncViewer::CallbackWrappers::OnChangeSampler);
	InitalizeObjectTypeCallbacks(MFn::kLayeredTexture, VSyncViewer::CallbackWrappers::OnChangeSampler);
	
	return true;
}

void VSyncViewer::ReleaseCallbacks() {

	if(VSyncViewer::g_ViewportSelectionListenerCallback != 0) {
		MEventMessage::removeCallback(VSyncViewer::g_ViewportSelectionListenerCallback);
		VSyncViewer::g_ViewportSelectionListenerCallback = 0;
	}

	if(VSyncViewer::g_ViewportAddSceneNodeCallback != 0) {
		MDagMessage::removeCallback(VSyncViewer::g_ViewportAddSceneNodeCallback);
		VSyncViewer::g_ViewportAddSceneNodeCallback = 0;
	}

	if(VSyncViewer::g_ViewportAddDGNodeCallback != 0) {
		MDGMessage::removeCallback(VSyncViewer::g_ViewportAddDGNodeCallback);
		VSyncViewer::g_ViewportAddDGNodeCallback = 0;
	}

	ReleaseRegisteredCallbacks();
}

void VSyncViewer::OnChangeSelection(void* clientData)
{
	bool updatedInterface = false;
	MObject object;

	try {
		
		MObject selectedTransform;
		MSelectionList viewportSelection;

		if(MAYA_FAIL(MGlobal::getActiveSelectionList(viewportSelection)))
			throw tException("Failed to get active selection list.");

		MObjectArray selectedObjects;
		uint n = viewportSelection.length();

		for(uint i = 0; i < n; i++) {

			MAYA_CONTINUE_IF_FAIL(viewportSelection.getDependNode(i, object))
			if(VUtil::IsTransform(object)) {
				if(!VUtil::GetTransformMesh(object, object))
					continue;
			}
			else if(!VUtil::IsMesh(object))
				continue;

			selectedObjects.append(object);

			if(i == 0) {
							
				/*
				When a new object is created in the viewport, attirbutes need to first be initalized before
				attempting to sync the object with the server
				*/
				if(!VSyncViewer::InitSelectedAttributes(object))
					throw tException("Init attributes error.");
				
				/*
				Update the interface to reflect current selection state
				*/
				VGUI::Refresh::SynchronizeSelection(object);
				VGUI::Refresh::SelectionParameters(object);
				VGUI::Refresh::ProceduralPopulation(object);
				VGUI::Refresh::DeformationParameters(object);
				updatedInterface = true;

				return;
			}
		}

		if(selectedObjects.length() > 0) {

			if(!VSyncViewer::SynchronizeSelection(selectedObjects))
				throw tException("Failed to synchronize with server.");
		}

		if(!VNet::SendScene_Selected(selectedObjects))
			throw tException("Failed to synchronize server selection.");
		
	} catch(tException exception) {

	}
	
	if(!updatedInterface) {

		/*
		Update the interface to reflect current selection state
		*/
		VGUI::Refresh::SynchronizeSelection(object);
		VGUI::Refresh::SelectionParameters(object);
		VGUI::Refresh::ProceduralPopulation(object);
		VGUI::Refresh::DeformationParameters(object);
	}
}

bool VSyncViewer::CheckMatch(MObject& targetObject, QuerysStructs::tGeometryHeader* pHeader) {

	if(!VUtil::IsMesh(targetObject) || !pHeader)
		return false;
	
	MPlug attribute;
	if(!VUtil::GetObjectAttribute(targetObject, MString("lv_format"), attribute))
		return false;

	int vertexFormat;
	if(MAYA_FAIL(attribute.getValue(vertexFormat)))
		throw tException("Failed to get valid vertex format.");
	
	/*uint localVertexFormat = 0;
	if(!VUtil::GetMeshVertexFormat(targetObject, localVertexFormat))
		return false;*/

	if(pHeader->vertexFormat != vertexFormat)
		return false;

	return true;
}

bool VSyncViewer::CheckMatch(MObject& targetObject, QuerysStructs::tGeometryPartHeader* pHeader, uint partIndex) {

	if(!VUtil::IsMesh(targetObject) || !pHeader)
		return false;
	
	/*
	Test consistancy in shader filename
	*/
	MPlug attributeShader;
	if(!VUtil::GetObjectAttribute(targetObject, MString("liveview_shader"), attributeShader)) 
		return false;
	
	MString localShaderString = attributeShader.asString();	
	MStringArray localShaderArray;
	localShaderString.split(';', localShaderArray);

	if(partIndex > localShaderArray.length() - 1)
		return false;

	if(!IsEqual(pHeader->shader.Buffer, localShaderArray[partIndex]))
		return false;

	/*
	Test consistancy in shader triangle assignment
	*/
	MFnMesh targetMesh(targetObject);
	MObjectArray shaderArray;
	MIntArray shaderIndicies;
	if(MAYA_FAIL(targetMesh.getConnectedShaders(0, shaderArray, shaderIndicies)))
		return false;

	if(partIndex > shaderArray.length() - 1 || partIndex > shaderIndicies.length() - 1)
		return false;

	uint shaderFaces = shaderIndicies[partIndex];
	if(shaderFaces != pHeader->triangeCount)
		return false;

	return true;
}

bool VSyncViewer::CheckMatch(MObject& targetObject, QuerysStructs::tChannelHeader* pHeader, uint partIndex) {

	if(!VUtil::IsMesh(targetObject) || !pHeader)
		return false;
	
	MString localChannelString;
	MString localPartString;
	MStringArray localChannelArray;
	MStringArray localPartArray;

	MString channels[] = {
		MString("liveview_color"),
		MString("liveview_normal"),
		MString("liveview_specular"),
	};
	
	MPlug attribute;
	for(uint i = 0; i < 3; i++) {

		if(!VUtil::GetObjectAttribute(targetObject, channels[i], attribute)) 
			return false;

		localChannelString = attribute.asString();
		localChannelString.split(';', localChannelArray);

		if(partIndex > localChannelArray.length() - 1)
			return false;

		localPartString = localChannelArray[partIndex];
		localPartString.split(',', localPartArray);
		
		QuerysStructs::tChannelHeader* pChannel = &pHeader[i];
		for(uint i = 0; i < 5; i++) {
		
			if(!IsEqual(pChannel->samplers[i].Buffer, localPartArray[i]))
				return false;		
		}

	}
	return true;
}

bool VSyncViewer::CheckMatch(MObject& targetObject, QuerysStructs::tInstancingHeader* pHeader) {

	if(!VUtil::IsMesh(targetObject) || !pHeader)
		return false;
	
	MPlug attribute;
	if(!VUtil::GetObjectAttribute(targetObject, MString("liveview_instanced"), attribute)) {

		return (pHeader->isInstanced == false);
	}

	bool localInstanced = (attribute.asInt() != 0);
	if(pHeader->isInstanced != localInstanced)
		return false;

	if(!VUtil::GetObjectAttribute(targetObject, MString("lv_instchildren"), attribute))
		return false;

	MString localInstanceChildrenString = attribute.asString();
	MStringArray localInstanceChildrenArray;
	localInstanceChildrenString.split(';', localInstanceChildrenArray);

	if(localInstanceChildrenArray.length() + 1 != pHeader->childCount)
		return false;

	return true;
}

bool VSyncViewer::CheckMatch(MObject& targetObject, QuerysStructs::tInstanceChildHeader* pHeader, uint childIndex) {

	if(!VUtil::IsMesh(targetObject) || !pHeader)
		return false;
	
	MPlug attribute;
	if(!VUtil::GetObjectAttribute(targetObject, MString("lv_instchildren"), attribute)) 
		return false;

	MString localInstancesString = attribute.asString();
	MStringArray localInstancesArray;
	localInstancesString.split(';', localInstancesArray);

	if(childIndex > localInstancesArray.length() - 1)
		return false;

	MString localInstanceString = localInstancesArray[childIndex];
	MStringArray localInstanceArray;
	localInstanceString.split(',', localInstanceArray);
	
	MString instanceName = localInstanceArray[0];
	if(!IsEqual(pHeader->name.Buffer, instanceName))
		return false;

	return true;
}

bool VSyncViewer::CheckMatch(MObject& targetObject, QuerysStructs::tDeformationHeader* pHeader) {

	if(!VUtil::IsMesh(targetObject) || !pHeader)
		return false;
	
	MPlug attribute;
	if(!VUtil::GetObjectAttribute(targetObject, MString("lv_defactive"), attribute)) {

		return (pHeader->isDeformation == false);
	}

	bool localDeformation = (attribute.asInt() != 0);
	if(pHeader->isDeformation != localDeformation)
		return false;

	if(!VUtil::GetObjectAttribute(targetObject, MString("lv_defscale"), attribute))
		return false;

	float localDeformationScale = attribute.asFloat();
	if(pHeader->scale != localDeformationScale)
		return false;

	return true;
}

bool VSyncViewer::IsObjectOnServer(MObject& targetObject) {

	byte* pData = NULL;
	try {

		if(!VUtil::IsMesh(targetObject))
			throw tException("Invalid parameter.");

		QuerysStructs::tViewerInfoQuery queryInfo;
		queryInfo.responceType = QUERY_RESPONCE_OBJECTEXISTS;
		if(!GetNameBuffer(&queryInfo.target, targetObject))
			throw tException("Failed to get query target.");

		uint size = 0;
		bool isPresent = false;
		if(VNet::QueryServer(&queryInfo, (char**)&pData, &size)) {

			QuerysStructs::tQueryHeader* pQuery = (QuerysStructs::tQueryHeader*)pData;
			isPresent = pQuery->isPresent;
			delete [] pData;
		}

		return isPresent;

	}catch(tException exception) {

		VPlugin::HandleException(&exception);
		if(pData)
			delete [] pData;

		return false;
	}

}

bool VSyncViewer::IsObjectSynchronized(MObject& targetObject, VSyncViewer::tSyncReport* pReport) {

	byte* pData = NULL;
	QuerysStructs::tViewerInfoQuery queryInfo;

	try {

		if(!VUtil::IsMesh(targetObject) || !pReport)
			throw tException("Invalid parameters.");

		MString targetName;
		if(!VUtil::GetObjectName(targetObject, targetName))
			throw tException("Failed to get query target.");

		queryInfo.responceType = QUERY_RESPONCE_SYNCHRONIZED;
		if(!GetNameBuffer(&queryInfo.target, targetName))
			throw tException("Failed to get query target.");

		UINT dataSize = 0;
		if(!VNet::QueryServer(&queryInfo, &pData, &dataSize))
			throw tException("Failed to query server.");

		if(dataSize < sizeof(QuerysStructs::tQueryHeader))
			throw tException("Invalid query data stream.");
		
		QuerysStructs::tQueryHeader* pHeader = (QuerysStructs::tQueryHeader*)pData;
		pData += sizeof(QuerysStructs::tQueryHeader);

		if(pHeader->isPresent ^ IsEqual(pHeader->targetName.Buffer, targetName))
			throw tException("Query header mismatch occoured.");
		
		/*
		If the server reports that this object is present on it, then do a more comprehensive
		analysis to determine what actually needs to be updated
		*/
		if(pHeader->isPresent) {

			/*
			I'm an optimist
			*/
			pReport->IsDeformation = true;
			pReport->IsGeometry = true;
			pReport->IsInstancing = true;
			pReport->IsParameters = true;

			/*
			Check geometry
			*/
			QuerysStructs::tGeometryHeader* pGeometry = (QuerysStructs::tGeometryHeader*)pData;
			if((uint)pGeometry - (uint)pHeader >= dataSize)
				throw tException("Geometry query data expected but not fully present.");

			pReport->IsGeometry = CheckMatch(targetObject, pGeometry);
			pData += sizeof(QuerysStructs::tGeometryHeader);

			/*
			Check geometry parts and assigned materials
			*/
			for(uint i = 0; i < (uint)max(0, pGeometry->partCount); i++) {

				QuerysStructs::tGeometryPartHeader* pGeometryPart = (QuerysStructs::tGeometryPartHeader*)pData;
				if((uint)pGeometryPart - (uint)pHeader >= dataSize)
					throw tException("Geometry part query data expected but not fully present.");

				if(!CheckMatch(targetObject, pGeometryPart, i))
					pReport->IsGeometry = false;

				if(!CheckMatch(targetObject, pGeometryPart->channel, i))
					pReport->IsParameters = false;

				pData += sizeof(QuerysStructs::tGeometryPartHeader);
			}

			/*
			Check instancing
			*/
			QuerysStructs::tInstancingHeader* pInstancing = (QuerysStructs::tInstancingHeader*)pData;
			if((uint)pInstancing - (uint)pHeader >= dataSize)
				throw tException("Instancing query data expected but not fully present.");

			pReport->IsInstancing = CheckMatch(targetObject, pInstancing);
			pData += sizeof(QuerysStructs::tInstancingHeader);

			/*
			Check instance children
			*/
			for(uint i = 0; i < (uint)max(0, pInstancing->childCount); i++) {

				QuerysStructs::tInstanceChildHeader* pInstanceChild = (QuerysStructs::tInstanceChildHeader*)pData;
				if((uint)pInstanceChild - (uint)pHeader >= dataSize)
					throw tException("Instance child query data expected but not fully present.");

				if(!CheckMatch(targetObject, pInstanceChild, i))
					pReport->IsInstancing = false;
				pData += sizeof(QuerysStructs::tInstanceChildHeader);
			}

			/*
			Check deformation
			*/
			QuerysStructs::tDeformationHeader* pDeformation = (QuerysStructs::tDeformationHeader*)pData;
			if((uint)pDeformation - (uint)pHeader >= dataSize)
				throw tException("Deformation query data expected but not fully present.");

			pReport->IsDeformation = CheckMatch(targetObject, pDeformation);
			pData += sizeof(QuerysStructs::tDeformationHeader);

		} 
		/*
		The target object doesn't exist on the server so signal that everything is out of sync
		*/
		else {

			pReport->IsDeformation = false;
			pReport->IsGeometry = false;
			pReport->IsInstancing = false;
			pReport->IsParameters = false;
		}

		if(pData)
			delete [] pData;

		return true;

	}catch(tException exception) {

		VPlugin::HandleException(&exception);
		if(pData)
			delete [] pData;

		return false;
	}
}

/**
	Ensures the scene is send to the server to ensure consistancy between Maya and it.

	@author Dacre Denny
	@return true if successful otherwise false
*/
bool VSyncViewer::EntireScene() {

	try {
		
		MObjectArray objects;
		if(VUtil::GetSceneMeshes(objects)) {

			for(uint i = 0; i < objects.length(); i++) {

				MObject targetObject = objects[i];
						
				if(!VNet::SendMesh_Geometry(targetObject, false))
					continue;
				/*if(!VNet::SendMesh_Parameters(targetObject))
					continue;*/
				/*if(!VNet::SendSamplers(targetObject))*/
					continue;
				/*if(!VNet::SendInstancing(targetObject))
					continue;*/
				/*if(!VNet::SendDeformation(targetObject))
					continue;*/
				
				MObject transform;
				VUtil::GetObjectTransform(targetObject, transform);
				if(!VNet::SendTransform(targetObject, transform))
					continue;
			}
		}

		if(VUtil::GetSceneLights(objects)) {

			for(uint i = 0; i < objects.length(); i++) {
			
				if(!VNet::SendScene_Light(objects[i], false))
					continue;
			}
		}
		return true;

	}catch(tException exception) {

		return false;
	}
}

/**
	Ensures that the server consistatly reflects the current selection in Maya.

	@author Dacre Denny
	@param objects the objects to synchronize with the server
	@return true if successful otherwise false
*/
bool VSyncViewer::SynchronizeSelection(MObjectArray& objects) {

	try {

		for(uint i = 0, n = objects.length(); i < n; i++) {

			MObject targetObject = objects[i];
			if(!VUtil::IsMesh(targetObject))
				throw tException("Invalid object type: %s", targetObject.apiTypeStr());

			/*
			Create a synchronization report, and query the server. If successful
			review the report to determine what, if anything in the current selection,
			needs to be updated on the server
			*/
			tSyncReport report;
			report.IsDeformation = false;
			report.IsGeometry = false;
			report.IsInstancing = false;
			report.IsParameters = false;
			/*if(!VSyncViewer::IsObjectSynchronized(targetObject, &report))
				throw tException("Failed to establish synchronization state.");*/

			if(!report.IsGeometry) {
					
				if(!VNet::SendMesh_Geometry(targetObject, false))
					throw tException("Failed to send mesh data.");	
			}

			if(!report.IsParameters) {
				
				//if(!VNet::SendMesh_Parameters(targetObject))
				//	throw tException("Failed to send parameter data.");

				/*if(!VNet::SendSamplers(targetObject))
					throw tException("Failed to send sampler data.");*/
			}

	/*		if(!report.IsInstancing) {
					
				if(!VNet::SendInstancing(targetObject))
					throw tException("Failed to send instancing data.");
			}*/

			/*if(!report.IsDeformation) {
					
				if(!VNet::SendDeformation(targetObject))
					throw tException("Failed to send deformation data.");	
			}*/

			/*
			Always send a transformation because it's quite cheap to do
			*/
			MObject targetTransform;
			if(!VUtil::GetMeshTransform(targetObject, targetTransform))
				throw tException("Failed to get object transform.");	

			if(!VNet::SendTransform(targetObject, targetTransform))
				throw tException("Failed to send transform data.");	
		}

		return true;
	}
	catch(tException exception) {

		VPlugin::HandleException(&exception);
		return false;
	}
}


void VSyncViewer::CallbackWrappers::RemoveObjectCallbacks(MObject& object) {

	MCallbackIdArray nodeCallbacks;
	if(MAYA_FAIL(MEventMessage::nodeCallbacks(object, nodeCallbacks)))
		return;

	if(nodeCallbacks.length() > 0) {

		if(MAYA_FAIL(MEventMessage::removeCallbacks(nodeCallbacks)))
			return;
	}

	if(MAYA_FAIL(MNodeMessage::nodeCallbacks(object, nodeCallbacks)))
		return;

	if(nodeCallbacks.length() > 0) {

		if(MAYA_FAIL(MNodeMessage::removeCallbacks(nodeCallbacks)))
			return;
	}

}

/**
	A special purpose function used to handle samplers that drive instancing. This requires analysis
	of the samplers name identifier to determine how and for what it is used

	@param instanceSampler sampler to send state and information of
	@param remove true to remove from server otherwise false
	@author Dacre Denny
*/
void VSyncViewer::HandleInstanceSampler(MObject& instanceSampler, bool remove) {

	/*MObject targetObject;
	MString channelName, childObject;

	if(!VUtil::GetInstanceSamplerTokens(instanceSampler, targetObject, childObject, channelName))
		return;

	VNet::SendInstancing(targetObject);
	MObject childMesh;
	if(VUtil::GetMeshByName(childObject,childMesh)) {
	
		if(!VSyncViewer::IsObjectOnServer(childMesh))
			VNet::SendMesh_Geometry(childMesh, false);
	}
	
	VNet::SendSampler(targetObject, instanceSampler, childObject, channelName, remove);*/
}

/**
	Used to handle surface samplers. This requires analysis of the samplers name identifier to 
	determine how and for what it is used

	@param sampler sampler to send state and information of
	@param remove true to remove from server otherwise false
	@author Dacre Denny
*/
//void VSyncViewer::HandleStandardSampler(MObject& sampler, bool remove) {
//
//	MObject targetObject;
//	MString targetChannel;
//	UINT partIndex;
//
//	if(!VUtil::GetStandardSamplerTokens(sampler, targetObject, &partIndex, targetChannel))
//		return;
//	
//	VNet::SendSampler(targetObject, sampler, partIndex, targetChannel, remove);
//}


/**
	Called when the user alters an attribute value in a sampler that has some relation to the live view system. Takes care of calling functionality
	that relates to the samplers purpose

	@author Dacre Denny
*/
void VSyncViewer::CallbackWrappers::OnChangeSampler(MNodeMessage::AttributeMessage msg, MPlug& plug, MPlug& otherPlug, void* pClientData) {

	if(CommonMsgProcess(msg, plug)) {

		return;
	}
	
	MStatus result;
	MObject samplerObject = plug.node();
	if(samplerObject.hasFn(MFn::kLayeredTexture)) {

		/* Basis of material/surface shading */
	}
	else {

		/*
		See if this sampler has a layered texture parent - ie determine if it's used for surface material or instancing
		*/
		if(plug.isConnected()) {
		
			MFnDependencyNode source(plug.node(), &result);
			MAYA_RETURN_IF_FAIL(result,);

			MPlug targetPlug = source.findPlug(MString("outColor"), &result);
			MAYA_RETURN_IF_FAIL(result,);

			MPlugArray plugArray;
			if(!targetPlug.connectedTo(plugArray, true, true, &result))
				MAYA_RETURN_IF_FAIL(result,);
			
			for(u_int i = 0, n = plugArray.length(); i < n; i++) {
		
				if(plugArray[i].node().apiType() == MFn::kLayeredTexture) {

					VNet::SendScene_SamplerChange(samplerObject);
					break;
				}
			}
		}
		else {

			if(VUtil::IsInstanceSampler(samplerObject)) {
				
				MObject parentObject;
				MObject childObject;
				MString channel;
				
				if(VUtil::GetInstanceSamplerTokens(samplerObject, parentObject, childObject, channel)) {

					//VNet::SendInstanceSampler(parentObject, samplerObject, channel, remove);
				}
			}
		}
	}
	
}

/**
	Called when the user alters an attribute of the repecitive light node. The function wraps a call to a network
	transmission that sends data about the respective light to the connected viewer

	@author Dacre Denny
*/
void VSyncViewer::CallbackWrappers::OnChangeLight(MNodeMessage::AttributeMessage msg, MPlug& plug, MPlug& otherPlug, void* pClientData) {
	
	if(CommonMsgProcess(msg, plug)) {
			
		if(msg & MNodeMessage::kConnectionBroken)
			VNet::SendScene_Light(plug.node(), true);
	}

	VNet::SendScene_Light(plug.node(), false);
}

/**
	Called when the user alters a transformation that is associated to a node in the scene's DAG.
	When invoked, this function checks the type of node that is attached to the respective transformation node and calls
	the appropriate handler functions

	@author Dacre Denny
*/
void VSyncViewer::CallbackWrappers::OnChangeTransform(MNodeMessage::AttributeMessage msg, MPlug& plug, MPlug& otherPlug, void* pClientData) {
	
	if(CommonMsgProcess(msg, plug))
		return;
	
	MObject transform = plug.node();
	if(VUtil::IsTransform(transform)) {

		MObject target;
		if(VUtil::GetTransformLight(transform, target))
			VNet::SendTransform(target, transform);
		else if(VUtil::GetTransformMesh(transform, target))
			VNet::SendTransform(target, transform);
	}
}

/**
	Called when the user applies a shader engine to a mesh. This is called for situations where the user drags a shader
	onto a small porition of a mesh. This callback enables the viewer to respond immediatly to these situations

	@author Dacre Denny
*/
//void VSyncViewer::CallbackWrappers::OnChangeMeshMaterial( MNodeMessage::AttributeMessage msg, MPlug& plug, MPlug& otherPlug, void* pClientData) {
//	
//	if(CommonMsgProcess(msg, plug))
//		return;
//
//	MObjectArray meshes;
//	VUtil::GetSceneMeshes(meshes);
//
//	for(uint i = 0; i < meshes.length(); i++) {
//
//		MFnMesh mesh(meshes[i]);
//		MObjectArray shaderEngines;
//		mesh.getConnectedShaders(0, shaderEngines, MIntArray());
//
//		for(uint j = 0; j < shaderEngines.length(); j++) {
//
//			if( shaderEngines[j] == plug.node() ) {
//
//				if(!VSyncViewer::IsObjectOnServer(meshes[i])) {
//					VNet::SendMesh_Geometry(meshes[i], false);
//					meshes.remove(i);
//					j = shaderEngines.length();
//					i = i <= 1 ? 0 : i - 1;
//				}
//
//				VNet::SendSamplers(plug.node());
//			}
//		}
//	}
//}

void VSyncViewer::CallbackWrappers::OnChangeMesh(MNodeMessage::AttributeMessage msg, MPlug& plug, MPlug& otherPlug, void* pClientData) {
	
	if(CommonMsgProcess(msg, plug)) {
			
		if(msg & MNodeMessage::kConnectionBroken)
			VNet::SendMesh_Geometry(plug.node(), true);
	}

	VNet::SendMesh_Geometry(plug.node(), false);
}

void VSyncViewer::CallbackWrappers::OnChangeUV(MNodeMessage::AttributeMessage msg, MPlug& plug, MPlug& otherPlug, void* pClientData) {

	if(CommonMsgProcess(msg, plug))
		return;

	MString name;	
	MStatus result;

	if(plug.isConnected()) {
		
		MFnDependencyNode dest(plug.node(), &result);
		MPlug targetPlug = dest.findPlug(MString("output"), &result);
		
		MPlugArray plugArray;
		MObject mesh;
		if(targetPlug.connectedTo(plugArray, true, true, &result)) {
		
			for(u_int i = 0, n = plugArray.length(); i < n; i++) {
				
				mesh = plugArray[i].node();
				if(mesh.apiType() == MFn::kMesh)
					VNet::SendMesh_Geometry(plug.node(), false);
			}
		}
	}
}